package cn.bugstack.springframework.context.annotation;

import cn.bugstack.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import cn.bugstack.springframework.beans.factory.config.BeanDefinition;
import cn.bugstack.springframework.beans.factory.support.BeanDefinitionRegistry;
import cn.bugstack.springframework.stereotype.Component;
import cn.hutool.core.util.StrUtil;

import java.util.Set;

/**
 * 自定义的类路径 Bean 定义扫描器，用于扫描指定包下所有的组件并注册为 Spring Bean。
 * 继承自 ClassPathScanningCandidateComponentProvider，增加了对 Bean 作用域解析和 Bean 名称确定的功能。
 */
public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {

    private BeanDefinitionRegistry registry;

    /**
     * 构造函数，初始化扫描器并传入 Bean 定义注册表。
     *
     * @param registry Bean 定义注册表，用于注册扫描到的 Bean 定义。
     */
    public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
        this.registry = registry;
    }

    /**
     * 执行扫描操作，对每个指定的基础包进行扫描，找到所有的候选组件并注册为 Bean 定义。
     *
     * @param basePackages 待扫描的基础包名数组。
     */
    public void doScan(String... basePackages) {
        for (String basePackage : basePackages) {
            // 在指定的包下查找候选组件（即带有特定注解的类）
            Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
            for (BeanDefinition beanDefinition : candidates) {
                // 解析 Bean 的作用域并设置
                String beanScope = resolveBeanScope(beanDefinition);
                if (StrUtil.isNotEmpty(beanScope)) {
                    beanDefinition.setScope(beanScope);
                }
                // 注册 Bean 定义到 Bean 定义注册表
                registry.registerBeanDefinition(determineBeanName(beanDefinition), beanDefinition);
            }
        }

        // 注册处理注解的 BeanPostProcessor，例如 @Autowired 和 @Value
        registry.registerBeanDefinition("cn.bugstack.springframework.context.annotation.internalAutowiredAnnotationProcessor", new BeanDefinition(AutowiredAnnotationBeanPostProcessor.class));
    }

    /**
     * 解析 Bean 定义的作用域。如果类上标注了 @Scope 注解，则提取其 value 值作为作用域。
     *
     * @param beanDefinition 待解析的 Bean 定义。
     * @return 返回解析出的作用域字符串，若未标注 @Scope 则返回空字符串。
     */
    private String resolveBeanScope(BeanDefinition beanDefinition) {
        Class<?> beanClass = beanDefinition.getBeanClass();
        Scope scope = beanClass.getAnnotation(Scope.class);
        if (null != scope) return scope.value();
        return StrUtil.EMPTY;
    }

    /**
     * 确定 Bean 的注册名称。优先使用 @Component 注解中的 value 值，若未指定则默认使用类名的首字母小写形式。
     *
     * @param beanDefinition 待确定名称的 Bean 定义。
     * @return 返回确定的 Bean 名称。
     */
    private String determineBeanName(BeanDefinition beanDefinition) {
        Class<?> beanClass = beanDefinition.getBeanClass();
        Component component = beanClass.getAnnotation(Component.class);
        String value = component.value();
        if (StrUtil.isEmpty(value)) {
            value = StrUtil.lowerFirst(beanClass.getSimpleName());
        }
        return value;
    }

}

